package com.mac.flightpath {
	import flash.display.CapsStyle;
	import flash.display.Graphics;
	import flash.display.JointStyle;
	import flash.display.LineScaleMode;
	import flash.geom.Bezier;
	import flash.geom.Point;
	
	/**
	 * Represents a set of beziers that denote the path an Aircraft should follow.
	 *  
	 */	
	public class Flightpath {
		
		/**
		 * The threshold controls the allowed proximity of points on the layer when peforming smoothing operations. 
		 */
		private static const THRESHOLD:Number = 6;
		private var thresholdPoint:Point;
		
		public var rawPoints:Array = new Array();
		public var filteredPoints:Array = new Array();
		public var beziers:Array = new Array();
		
		/**
		 * Empties out all arrays. 
		 * 
		 */		
		public function reset():void {
			rawPoints = new Array();
			filteredPoints = new Array();
			beziers = new Array();
		}
		
		/**
		 * Adds a point to the rawPoints array. Typically these are points captured by the mouse.
		 *  
		 * @param point
		 * 
		 */		
		public function addPoint(point:Point):void {
			rawPoints.push(point);
		}
		
		/**
		 * The draw() function will first remove any uneccessary points and subsequently create a set of beziers using 
		 * the remaining points. 
		 * 
		 * The midpoints of each line connecting the points will be used as anchor points for the beziers.
		 *  
		 * @param graphics
		 * 
		 */		
		public function draw(graphics:Graphics):void {
			removeBadPoints();
			createBeziers();
			graphics.lineStyle(2,0xFF0000,1,true,LineScaleMode.NORMAL,CapsStyle.ROUND,JointStyle.ROUND);

			var firstBezier:Bezier = beziers[0]; 
			graphics.moveTo(firstBezier.start.x, firstBezier.start.y);

			for each(var bezier:Bezier in beziers) {
				graphics.curveTo(bezier.control.x, bezier.control.y,bezier.end.x, bezier.end.y);
			}
			
			graphics.lineStyle();
		}
		
		/**
		 * Takes only neccessary points from the rawPoints array and puts them into the
		 * filteredPoints array. A point is considered neccessary if it falls outside of the
		 * range of another point based on the value of the THRESHOLD constant.
		 * 
		 */		
		protected function removeBadPoints():void {
			for (var i:int=0; i<rawPoints.length; i++) {
				var point:Point = rawPoints[i] as Point;
				
				if(i>0 && i<rawPoints.length-1) {
					
					if(!thresholdPoint) {
						thresholdPoint = rawPoints[i-1];
					}
					
					if(!isToClose(thresholdPoint,point)) {
						filteredPoints.push(point);
						thresholdPoint = null;
					}
				} else {
					filteredPoints.push(point);
				}
				
			}
		}
		
		/**
		 * Uses the filteredPoints array to constuct an array beziers. The beziers
		 * are created by using the midpoints of each line joining the points. 
		 * 
		 */		
		protected function createBeziers():void {
			
			if(filteredPoints.length > 0) {
				
				for(var i:int=0;i<filteredPoints.length-2;i++) {
					var bezier:Bezier;
					if(i==0) {
						bezier = getFirstBezier();
					} else if((i+2) == filteredPoints.length-1) { 
						bezier = getLastBezier();
					} else { 
						bezier = getBezier(i);
					}
					
					beziers.push(bezier);
					
				}
				
			}
		}
		
		
		protected function getFirstBezier():Bezier {
			var start:Point = filteredPoints[0];
			var control:Point = filteredPoints[1];
			var end:Point = Point.interpolate(control, filteredPoints[2],0.5);
			return new Bezier(start,control,end); 
		}
		
		protected function getLastBezier():Bezier {
			var index:int = filteredPoints.length-3;
			var start:Point = Point.interpolate(filteredPoints[index],filteredPoints[index+1],0.5);
			var control:Point = filteredPoints[index];
			var end:Point = filteredPoints[index+2];
			return new Bezier(start,control,end); 
		}
		
		protected function getBezier(index:int):Bezier {
			var start:Point = Point.interpolate(filteredPoints[index],filteredPoints[index+1],0.5);
			var control:Point = filteredPoints[index+1];
			var end:Point = Point.interpolate(control, filteredPoints[index+2],0.5);
			return new Bezier(start,control,end); 
		}
		
		protected function isToClose(a:Point,b:Point):Boolean {
			if( ( (a.x < b.x) && (a.x + THRESHOLD > b.x) ) || 
				( (a.x > b.x) && (a.x - THRESHOLD < b.x) ) ||
				( (a.y < b.y) && (a.y + THRESHOLD > b.y) ) ||
				( (a.y > b.y) && (a.y - THRESHOLD < b.y) )
			  ) {
				return true;
			}
			return false;
		}
		
		
	}
}